/**
 * @file app_log.cpp
 * @author Forairaaaaa
 * @brief
 * @version 0.1
 * @date 2023-11-04
 *
 * @copyright Copyright (c) 2023
 *
 */
#include "app_log.h"
#include "spdlog/spdlog.h"
#include "../../hal/hal.h"
#include "../assets/theme/theme.h"
#include "network_log.h"

#ifdef ESP_PLATFORM
#include <Arduino.h>
#include <FS.h>
#include <SD.h>
#include <ESP32-TWAI-CAN.hpp>
#include "../utils/system/inputs/inputs.h"
#endif

using namespace MOONCAKE::APPS;
using namespace SYSTEM::INPUTS;

enum
{
    BRIGHTNESS_STATE_NORMAL = 0,
    BRIGHTNESS_STATE_HALF = 1,
    BRIGHTNESS_STATE_LOW = 2
};
int brightness_timeout_half = 30; // 超时时间（半亮）
int brightness_timeout_low = 60;  // 超时时间（微亮）
int brightness_val_half = 40;     // 亮度值（半亮）
int brightness_val_low = 2;       // 亮度值（微亮）
int brightness_state = 0;
int brightness_state_last = 0;
int brightness_cnt = 0;

#define PAGE_INDEX_MAX 2
int _page_index = 0;

class TextBuffer
{
public:
    int max_str_length = 200;
    int buff_size = 10;
    void putString(const String &str);
    String getString(void);

private:
    String uartStringBuffer[10];
    int uartStringIndex = 0;
};

void TextBuffer::putString(const String &str)
{
    if (str.length() > max_str_length)
    {
        spdlog::info("putString length overflow!");
        uartStringBuffer[uartStringIndex] = str.substring(0, max_str_length);
    }
    else
    {
        uartStringBuffer[uartStringIndex] = str;
    }
    uartStringIndex = (uartStringIndex + 1) % buff_size;
}

String TextBuffer::getString(void)
{
    String str = "";
    for (int i = 0; i < buff_size; i++)
    {
        int index = (uartStringIndex - 1 - i + buff_size) % buff_size;
        if (uartStringBuffer[index].length() > 0 && uartStringBuffer[index].length() <= max_str_length)
        {
            str += uartStringBuffer[index];
            str += "\n";

            // 最多保留200个字符，否则空间可能不够
            if (str.length() >= 200)
                break;
        }
        else if (uartStringBuffer[index].length() > max_str_length)
        {
            str += uartStringBuffer[index];
            str = str.substring(0, max_str_length);
            break;
        }
    }
    return str;
}

TextBuffer uartTextBuffer;
TextBuffer canTextBuffer;

#define PIN_CAN_TX 1
#define PIN_CAN_RX 2
#define PIN_LOGSERIAL_TX 9
#define PIN_LOGSERIAL_RX 10
// #define MAX_FILE_SIZE 10 * 1024 * 1024 // 10MB

#define FILE_UART_PREFIX "UART"
#define FILE_UART_SUFFIX ".txt"
#define FILE_CAN__PREFIX "CAN"
#define FILE_CAN__SUFFIX ".txt"

typedef struct
{
    const char *file_prefix;
    const char *file_suffix;
    char fileName[20];
    char fileNameMini[20];
    unsigned long fileIndex;
    unsigned long fileIndexMini;
    unsigned long bytesWritten;
    unsigned long g_file_size;
    File file;
} fileLog_t;

fileLog_t logFileUart;
fileLog_t logFileCan;

HardwareSerial logSerial(1);
CanFrame rxFrame;

static String str_display = "";

Button btn_up(GAMEPAD::BTN_UP);
Button btn_down(GAMEPAD::BTN_DOWN);
Button btn_left(GAMEPAD::BTN_LEFT);
Button btn_right(GAMEPAD::BTN_RIGHT);
Button btn_ok(GAMEPAD::BTN_A);
Button btn_exit(GAMEPAD::BTN_B);

void findMaxFileIndex(fileLog_t *log);
void createNewLogFile(fileLog_t *log);
void createOrOpenLogFile(fileLog_t *log);
void logData(fileLog_t *log, const char *data, unsigned int length);
int checkSDCardFull(void);

// Like setup()...
void AppLog::onResume()
{
    spdlog::info("{} onResume", getAppName());

    HAL::GetDisplay()->setBrightness(HAL::GetSystemConfig().brightness);

    HAL::DisplayPageString(20, 100, "Checking SD Card");

    logSerial.begin((HAL::GetSystemConfig().baud_uart), SERIAL_8N1, PIN_LOGSERIAL_RX, PIN_LOGSERIAL_TX);
    logSerial.setDebugOutput(true);

    if (!ESP32Can.begin(ESP32Can.convertSpeed((HAL::GetSystemConfig().baud_can)), PIN_CAN_TX, PIN_CAN_RX, 1024, 1024))
    {
        spdlog::info("CAN bus failed!");
    }

    logFileUart.file_prefix = FILE_UART_PREFIX;
    logFileUart.file_suffix = FILE_UART_SUFFIX;
    findMaxFileIndex(&logFileUart);    // 查找现有的最大文件索引
    createOrOpenLogFile(&logFileUart); // 创建或打开日志文件

    logFileCan.file_prefix = FILE_CAN__PREFIX;
    logFileCan.file_suffix = FILE_CAN__SUFFIX;
    findMaxFileIndex(&logFileCan);    // 查找现有的最大文件索引
    createOrOpenLogFile(&logFileCan); // 创建或打开日志文件

    network_log_init();

    brightness_cnt = millis();
}

// Like loop()...
void AppLog::onRunning()
{
    static uint32_t lastStamp[2] = {0};

    network_log_handle();

    if (logSerial.available() && HAL::GetSystemConfig().rec_uart_flag == 1)
    {
        // 将接收到的串口字符串数据保存
        String data = logSerial.readStringUntil('\n');

        // 打印时间到字符串最前方
        char time_str[50];
        strftime(time_str, sizeof(time_str), "[%Y-%m-%d %H:%M:%S]", HAL::GetLocalTime());
        String time = String(time_str);
        String saveStr = "";

        if (HAL::GetSystemConfig().add_time_to_log_uart == 1)
        {
            saveStr = time + " " + data; // 拼接时间和具体的内容
        }
        else
        {
            saveStr = data;
        }
        logData(&logFileUart, saveStr.c_str(), saveStr.length());

        // 放入缓冲区中，等待屏幕打印出来
        uartTextBuffer.putString(data);
        // 输出到telnet终端
        telnet_send("Uart", saveStr);
    }

    // 读取CAN报文，收到一帧则保存一帧，0-不等待
    if (ESP32Can.readFrame(rxFrame, 0) && HAL::GetSystemConfig().rec_can_flag == 1)
    {
        // 转换CAN数据为字符串
        String canDataStr;
        canDataStr = canDataStr + String(rxFrame.identifier, HEX) + " " + String(rxFrame.data_length_code) + " ";
        for (int i = 0; i < rxFrame.data_length_code; i++)
        {
            if (rxFrame.data[i] < 0x10)
            { // 如果是一位数则补个0
                canDataStr += "0";
            }
            canDataStr += String(rxFrame.data[i], HEX);
        }
        canDataStr.toUpperCase(); // 转换为大写

        // 打印时间到字符串最前方
        char time_str[50];
        strftime(time_str, sizeof(time_str), "[%Y-%m-%d %H:%M:%S]", HAL::GetLocalTime());
        String time = String(time_str);
        String saveStr = "";

        if (HAL::GetSystemConfig().add_time_to_log_can == 1)
        {
            saveStr = time + " " + canDataStr + "\n";
        }
        else
        {
            saveStr = canDataStr + "\n";
        }
        logData(&logFileCan, saveStr.c_str(), saveStr.length());

        // 放入缓冲区中，等待屏幕打印出来
        canTextBuffer.putString(canDataStr);
        // 输出到telnet终端
        telnet_send("Shell", saveStr);
    }

    uint32_t currentStamp = millis();
    // 计算熄屏时间
    if (currentStamp - lastStamp[0] > 200)
    {
        lastStamp[0] = currentStamp;

        int time_div = currentStamp - brightness_cnt;
        if (time_div > 0 && time_div <= brightness_timeout_half * 1000)
            brightness_state = BRIGHTNESS_STATE_NORMAL;
        else if (time_div > brightness_timeout_half * 1000 && time_div <= brightness_timeout_low * 1000)
            brightness_state = BRIGHTNESS_STATE_HALF;
        else if (time_div > brightness_timeout_low * 1000)
            brightness_state = BRIGHTNESS_STATE_LOW;

        // 正常亮度切换到半亮模式时
        if (brightness_state == BRIGHTNESS_STATE_HALF && brightness_state_last == BRIGHTNESS_STATE_NORMAL)
        {
            spdlog::info("half light mode");
            HAL::GetDisplay()->setBrightness(brightness_val_half);
        }
        // 半亮切换到全灭时
        else if (brightness_state == BRIGHTNESS_STATE_LOW && brightness_state_last == BRIGHTNESS_STATE_HALF)
        {
            spdlog::info("low light mode");
            HAL::GetDisplay()->setBrightness(brightness_val_low);
        }
        // 从低亮度切换到全亮的时候
        else if (brightness_state == BRIGHTNESS_STATE_NORMAL && brightness_state_last == BRIGHTNESS_STATE_LOW)
        {
            spdlog::info("normal light mode");
            HAL::GetDisplay()->setBrightness(HAL::GetSystemConfig().brightness);
        }
        // 从半亮度切换到全亮的时候
        else if (brightness_state == BRIGHTNESS_STATE_NORMAL && brightness_state_last == BRIGHTNESS_STATE_HALF)
        {
            spdlog::info("normal light mode");
            HAL::GetDisplay()->setBrightness(HAL::GetSystemConfig().brightness);
        }
        brightness_state_last = brightness_state;
    }

    // 刷新屏幕显示数据
    if (currentStamp - lastStamp[1] > 500)
    {
        lastStamp[1] = currentStamp;
        if (_page_index == 0)
        {
            str_display =
                "P" + String(_page_index) + " " + "LOG INFO" + "\r\n" +                                                                                            //
                "--------------------" + "\r\n" +                                                                                                                  //
                "WIFI: " + wifiGetSSid() + "\r\n" +                                                                                                                //
                "IP  : " + wifiGetIpStr() + "\r\n" +                                                                                                               //
                "UART: " + String(logFileUart.fileName) + "\r\n" +                                                                                                 //
                "SIZE: " + String(logFileUart.g_file_size / 1024.0f) + " kb" + "\r\n" +                                                                            //
                "CAN : " + String(logFileCan.fileName) + "\r\n" +                                                                                                  //
                "SIZE: " + String(logFileCan.g_file_size / 1024.0f) + " kb" + "\r\n" +                                                                             //
                "TIME: " + "UART>" + String(HAL::GetSystemConfig().add_time_to_log_uart) + " CAN>" + String(HAL::GetSystemConfig().add_time_to_log_can) + "\r\n" + //
                "REC : " + "UART>" + String(HAL::GetSystemConfig().rec_uart_flag) + " CAN>" + String(HAL::GetSystemConfig().rec_can_flag) + "\r\n"                 //
                ;
            HAL::DisplayPageString(1, 1, str_display.c_str());
        }
        else if (_page_index == 1)
        {
            // 屏幕中一行是20个字符，一个屏幕可以显示10行
            str_display =
                "P" + String(_page_index) + " " + "UART MONITOR" + "\r\n" + //
                "--------------------" + "\r\n"                             //
                ;
            str_display += uartTextBuffer.getString();
            HAL::DisplayPageString(1, 1, str_display.c_str());
        }
        else if (_page_index == 2)
        {
            str_display =
                "P" + String(_page_index) + " " + "CAN MONITOR" + "\r\n" + //
                "--------------------" + "\r\n"                            //
                ;
            str_display += canTextBuffer.getString();
            HAL::DisplayPageString(1, 1, str_display.c_str());
        }
    }

    // 当亮度值最低时，本次按键用于点亮屏幕，不响应任何动作
    if (brightness_state == BRIGHTNESS_STATE_LOW || brightness_state == BRIGHTNESS_STATE_HALF)
    {
        if (btn_left.released() || btn_right.released() || btn_ok.released() || btn_exit.released() || btn_up.released() || btn_down.released())
        {
            spdlog::info("button change light mode");
            brightness_cnt = millis();
        }
    }
    else
    {
        if (btn_left.released())
        {
            brightness_cnt = millis();
            _page_index--;
            if (_page_index < 0)
                _page_index = PAGE_INDEX_MAX;

            // 让屏幕立刻刷新一次
            lastStamp[1] -= 1000;
        }
        else if (btn_right.released())
        {
            brightness_cnt = millis();
            _page_index++;
            if (_page_index > PAGE_INDEX_MAX)
                _page_index = 0;

            // 让屏幕立刻刷新一次
            lastStamp[1] -= 1000;
        }
        else if (btn_ok.released())
        {
            brightness_cnt = millis();
        }
        else if (btn_exit.released())
        {
            brightness_cnt = millis();
            destroyApp();
        }
        else if (btn_up.released())
        {
            brightness_cnt = millis();
        }
        else if (btn_down.released())
        {
            brightness_cnt = millis();
        }
    }

    // HAL::Delay(1);
}

void AppLog::onDestroy()
{
    spdlog::info("{} onDestroy", getAppName());
    HAL::GetDisplay()->setBrightness(HAL::GetSystemConfig().brightness);
    ESP32Can.end();
}

//--------------------------------------------------------------------------------------------------------------------------------

void logData(fileLog_t *log, const char *data, unsigned int length)
{
    // 写入数据到文件
    log->file.write((const uint8_t *)data, length);
    log->bytesWritten += length;

    // 刷新文件系统缓存
    log->file.flush();

    log->g_file_size = log->bytesWritten;
    // spdlog::info("save to:{}, size:{:.2f}kb, str:{}", log->fileName, (log->bytesWritten / 1024.0f), data);

    // 检查文件大小
    if (log->bytesWritten >= (HAL::GetSystemConfig().file_max_size * 1024 * 1024))
    {
        spdlog::info("log file size is > {}, new file will create", (HAL::GetSystemConfig().file_max_size * 1024 * 1024));
        log->file.close();
        createNewLogFile(log);
    }
}

void findMaxFileIndex(fileLog_t *log)
{
    File root = SD.open("/");
    while (true)
    {
        File entry = root.openNextFile();
        if (!entry)
        {
            // 没有更多的文件
            break;
        }
        if (!entry.isDirectory())
        {
            String fie_name = entry.name();
            if (fie_name.startsWith(log->file_prefix) && fie_name.endsWith(log->file_suffix))
            {
                String indexStr = fie_name.substring(strlen(log->file_prefix), fie_name.length() - strlen(log->file_suffix));
                unsigned int index = indexStr.toInt();
                if (index >= log->fileIndex)
                {
                    log->fileIndex = index;
                }
            }
        }
        entry.close();
    }
    root.close();

    spdlog::info("log {}, findMaxFileIndex {}", log->file_prefix, log->fileIndex);
}

// 找到文件系统中序号最小的序号
void findMiniFileIndex(fileLog_t *log)
{
    log->fileIndexMini = 65535;
    File root = SD.open("/");
    while (true)
    {
        File entry = root.openNextFile();
        if (!entry)
        {
            // 没有更多的文件
            break;
        }
        if (!entry.isDirectory())
        {
            String fie_name = entry.name();
            if (fie_name.startsWith(log->file_prefix) && fie_name.endsWith(log->file_suffix))
            {
                String indexStr = fie_name.substring(strlen(log->file_prefix), fie_name.length() - strlen(log->file_suffix));
                unsigned int index = indexStr.toInt();
                if (index <= log->fileIndexMini)
                {
                    log->fileIndexMini = index;
                }
            }
        }
        entry.close();
    }
    root.close();

    spdlog::info("log {}, findMiniFileIndex {}", log->file_prefix, log->fileIndexMini);
}

void checkAndDeleteFile(fileLog_t *log)
{
    int error_cnt = 0;
    // 检查空间是否满了,如果满了则尝试删除index最小的文件，知道空间充足为止
    while (checkSDCardFull() == 1)
    {
        // 如果满了则找出最小的1个index的文件，删除后再新建文件
        findMiniFileIndex(log);
        sprintf(log->fileNameMini, "/%s%03d%s", log->file_prefix, log->fileIndexMini, log->file_suffix);
        if (SD.exists(log->fileNameMini))
        {
            if (SD.remove(log->fileNameMini))
            {
                spdlog::info("delete file: {} successful", log->fileNameMini);
            }
            else
            {
                spdlog::info("delete file: {} failed", log->fileNameMini);
            }
        }
        else
        {
            spdlog::info("file is not exists: {}", log->fileNameMini);
        }

        error_cnt++;
        if (error_cnt > 5)
        {
            HAL::PopFatalError("SD Card remove error");
        }
    }
}

void createNewLogFile(fileLog_t *log)
{
    // 检查空间，如果空间不足则删除最小index的文件
    checkAndDeleteFile(log);

    // 生成文件名
    log->fileIndex += 1;
    sprintf(log->fileName, "/%s%03d%s", log->file_prefix, log->fileIndex, log->file_suffix);

    spdlog::info("createNewLogFile open file name: {}", log->fileName);

    // 打开文件
    log->file = SD.open(log->fileName, FILE_APPEND, true);
    if (!log->file)
    {
        spdlog::error("Create newfile failed, Error opening file!");
        return;
    }

    log->bytesWritten = 0;
}

void createOrOpenLogFile(fileLog_t *log)
{
    // 检查空间，如果空间不足则删除最小index的文件
    checkAndDeleteFile(log);

    if (log->fileIndex < 0)
    {
        spdlog::error("createOrOpenLogFile error, file index {}", log->fileIndex);
        return;
    }

    while (true)
    {
        sprintf(log->fileName, "/%s%03d%s", log->file_prefix, log->fileIndex, log->file_suffix);
        spdlog::info("attemp to open: {}", log->fileName);
        // 尝试打开现有的最大索引文件，如果不存在则直接创建
        log->file = SD.open(log->fileName, FILE_APPEND, true);
        log->file.flush();
        log->file.close();
        log->file = SD.open(log->fileName, FILE_APPEND, true);
        if (log->file)
        {
            log->bytesWritten = log->file.size();
            log->g_file_size = log->bytesWritten;
            spdlog::info("file large is: {}", log->bytesWritten);

            if (log->bytesWritten < (HAL::GetSystemConfig().file_max_size * 1024 * 1024))
            {
                spdlog::info("logging to: {}", log->fileName);
                break;
            }
            else
            {
                log->file.close();
                // 如果该文件大于了体积，则序号++后重新来一次
                log->fileIndex += 1;
                spdlog::info("file is to large, will create new files");
            }
        }
        else
        {
            spdlog::error("create file error");
            break;
        }
    }
}

int checkSDCardFull(void)
{
    uint64_t freeSize = SD.totalBytes() - SD.usedBytes();

    // if (SD.usedBytes() > 40 * 1024 * 1024)
    if (freeSize < 500 * 1024 * 1024) // 保留500mb的空间
    {
        spdlog::error("no freeSize! used Bytes: {}", SD.usedBytes());
        return 1;
    }
    return 0;
}

//--------------------------------------------------------------------------------------------------------------------------------

// String data = logSerial.readStringUntil('\n');
// spdlog::info("uart_save_to_sdcard");

// File file = SD.open("/log_1.txt", FILE_APPEND, true);
// if (!file)
// {
//     spdlog::info("file open error");
//     logSerial.println("file open error");
//     return;
// }

// file.println(data);

// file.close();

// if (fileIndex > 0)
// {
//     sprintf(fileName, "/%s%03d%s", FILE_NAME_PREFIX, fileIndex, FILE_NAME_SUFFIX);
//     spdlog::info("attemp to open: {}", fileName);
//     // 尝试打开现有的最大索引文件，如果不存在则直接创建
//     logFile = SD.open(fileName, FILE_APPEND, true);
//     if (logFile)
//     {
//         bytesWritten = logFile.size();
//         if (bytesWritten < MAX_FILE_SIZE)
//         {
//             spdlog::info("logging to: {}", fileName);
//             return;
//         }
//         else
//         {
//             logFile.close();
//             // 如果该文件大于了体积，则序号++后重新来一次
//             fileIndex += 1;
//             sprintf(fileName, "/%s%03d%s", FILE_NAME_PREFIX, fileIndex, FILE_NAME_SUFFIX);
//         }
//     }
// }

// spdlog::info("no file name {} , will create newfile ", fileName);

// // 创建新文件
// createNewLogFile();
// spdlog::info("file save to:{}, large:{}, data:{}", fileName, bytesWritten, data.c_str());
// logSerial.println("file save: " + data);

// using namespace HAL_Rachel;

// static const String _log_path = "/11111.txt";
// HardwareSerial logSerial(1);

// void HAL_Rachel:: uart_save_to_sdcard()
// {
//             if (logSerial.available())
//         {
//             String data = logSerial.readStringUntil('\n');
//             // HAL_Rachel::uart_save_to_sdcard(data);

//             // spdlog::info("uart_save_to_sdcard");

//             File file = SD.open("/log_1.txt", FILE_WRITE);
//             if (!file)
//             {
//                 // spdlog::info("file open error");
//                 logSerial.println("file open error");
//                 return;
//             }

//             file.println(data);

//             file.close();

//             // spdlog::info("file save: " + data);
//             logSerial.println("file save: " + data);
//         }

//     // spdlog::info("uart_save_to_sdcard");

//     // // Open file
//     // File config_file = LittleFS.open(_log_path, FILE_WRITE, true);
//     // if (!config_file)
//     //     popFatalError("创建配置文件失败");

//     // config_file.println(data);

//     // config_file.close();

//     // spdlog::info("file save");
// }

// // #if CONFIG_FREERTOS_UNICORE
// // #define ARDUINO_RUNNING_CORE 0
// // #else
// #define ARDUINO_RUNNING_CORE 1
// // #endif

// // void TaskBlink(void *pvParameters);

// void TaskBlink(void *pvParameters)
// {
//     uint32_t blink_delay = *((uint32_t *)pvParameters);

//     logSerial.begin(115200, SERIAL_8N1, 10, 9);

//     // spdlog::info("TaskBlink init");
//     logSerial.println("TaskBlink init");
//     for (;;)
//     {
//         // HAL_Rachel::uart_save_to_sdcard();

//         if (logSerial.available())
//         {
//             String data = logSerial.readStringUntil('\n');
//             // HAL_Rachel::uart_save_to_sdcard(data);

//             // spdlog::info("uart_save_to_sdcard");

//             File file = SD.open("/log_1.txt", FILE_WRITE, true);
//             if (!file)
//             {
//                 // spdlog::info("file open error");
//                 logSerial.println("file open error");
//                 return;
//             }

//             file.println(data);

//             file.close();

//             // spdlog::info("file save: " + data);
//             logSerial.println("file save: " + data);
//         }

//         spdlog::info("TaskBlink");

//         logSerial.println("TaskBlink");

//         delay(blink_delay);
//     }
// }

// void HAL_Rachel::user_func_init()
// {
//     spdlog::info("user_func_init");
//     // Set up two tasks to run independently.
//     uint32_t blink_delay = 5000; // Delay between changing state on LED pin
//     // xTaskCreate(
//     //     TaskBlink, "Task Blink" // A name just for humans
//     //     ,
//     //     2048 // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
//     //     ,
//     //     (void *)&blink_delay // Task parameter which can modify the task behavior. This must be passed as pointer to void.
//     //     ,
//     //     2 // Priority
//     //     ,
//     //     NULL // Task handle is not used here - simply pass NULL
//     // );
//     xTaskCreatePinnedToCore(
//         TaskBlink, "Task Blink" // A name just for humans
//         ,
//         2048 // The stack size can be checked by calling `uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL);`
//         ,
//         (void *)&blink_delay // Task parameter which can modify the task behavior. This must be passed as pointer to void.
//         ,
//         2 // Priority
//         ,
//         NULL, // Task handle is not used here - simply pass NULL
//         1
//     );

// }
